#region Copyright (c) 2003-2004 ComponentGo!
/*
*********************************************************************
*                                                                   *
*       ComponentGo! Component Library                              *
*                                                                   *
*       Copyright (c) 2003-2004 ComponentGo!                        *
*       ALL RIGHTS RESERVED                                         *
*                                                                   *
*********************************************************************
*/
#endregion

using System;
using System.Collections;
using System.ComponentModel;

namespace ComponentGo.Calendars {

	/// <summary>
	/// A collection of appointments managed by a calendar.
	/// <seealso cref="RootCalendar.Appointments"/>
	/// </summary>
	/// <remarks>
	/// Basic operations are adding one new appointment
	/// <see cref="Add"/> or adding a group of appointments <see cref="AddRange"/>.
	/// A <see cref="Remove"/> method is used to remove an appointment from a calendar.<para/>
	/// Appointmets are read with a <see cref="P:Item(System.Int32)"/> indexer
	/// or with an enumerator.
	/// </remarks>
	[System.Runtime.InteropServices.ComVisible(true)]
	public class AppointmentCollection : CollectionBase {

		#region General

		internal BaseCalendar calendar = null;

		#endregion

		#region IList

		/// <summary>
		/// Adds a new Appointment to a collection.
		/// </summary>
		/// <param name="ap">A newly added Appointment.</param>
		/// <returns>Index of a newly added Appointment in a collection .</returns>
		public int Add(Appointment ap) {
			return List.Add(ap);
		}

		/// <summary>
		/// Adds a number of new CalendarAppointments to a collection.
		/// </summary>
		/// <param name="aps">A table of CalendarAppointments being added.</param>
		public void AddRange(Appointment [] aps) {
			foreach (Appointment ap in aps)
				ap.coll = this;
			InnerList.AddRange(aps);
			resync(true);
		}

		/// <summary>
		/// Checks whether an Appointment is already in the collection.
		/// </summary>
		/// <param name="ap">An appointmenr object being checked.</param>
		/// <returns><c>true</c> means an object is already in a collection.</returns>
		public bool Contains(Appointment ap) {
			return List.Contains(ap);
		}

		/// <summary>
		/// Returns an index of an object in a collection.
		/// </summary>
		/// <param name="ap">An Appointment object being checked.</param>
		/// <returns>If an object is found returna an index of the object in a collection. 
		///	returns -1 otherwise.</returns>
		public int IndexOf(Appointment ap) {
			return List.IndexOf(ap);
		}

		/// <summary>
		/// Inserts an Appointment to a collection in a given place.
		/// </summary>
		/// <param name="index">Index, an object will have after it is inserted.</param>
		/// <param name="ap">An Appointment object being inserted.</param>
		public void Insert(int index, Appointment ap) {
			List.Insert(index, ap);
		}

		/// <summary>
		/// Removes an object from a collection.
		/// </summary>
		/// <param name="ap">An Appointment object being removed.</param>
		public void Remove(Appointment ap) {
			List.Remove(ap);
		}

		/// <summary>
		/// Indexer allowing for reading an Appointment from an writing an Appointment to a collection
		/// under a given index.
		/// </summary>
		/// <param name="index">An index of an appoinment in a collection.</param>
		/// <value>An Appointment being set.</value>
		public Appointment this[int index] {
			get { return (Appointment)List[index]; }
			set { List[index] = value; }
		}

		#endregion

		#region Overrides

		/// <summary>
		/// Implementation of <see cref="CollectionBase"/>.
		/// </summary>
		protected override void OnClear() {
			base.OnClear();
			foreach (Appointment ap in List)
				ap.coll = null;
			resync(true);
		}

		/// <summary>
		/// Implementation of <see cref="CollectionBase"/>.
		/// </summary>
		protected override void OnInsert(int index, object value) {
			base.OnInsert(index, value);
			((Appointment)value).coll = this;
			resync(true);
		}

		/// <summary>
		/// Implementation of <see cref="CollectionBase"/>.
		/// </summary>
		protected override void OnRemove(int index, object value) {
			base.OnRemove(index, value);
			((Appointment)value).coll = null;
			if (index>0)
				this[index-1].LinkWithNext = false;
			resync(true);
		}

		/// <summary>
		/// Implementation of <see cref="CollectionBase"/>.
		/// </summary>
		protected override void OnSet(int index, object oldValue, object newValue) {
			base.OnSet(index, oldValue, newValue);
			((Appointment)oldValue).coll = null;
			((Appointment)newValue).coll = this;
			resync(true);
		}

		/// <summary>
		/// Implementation of <see cref="CollectionBase"/>.
		/// </summary>
		protected override void OnValidate(object value) {
			if (!(value is Appointment))
				throw new ArgumentException("Incorrect type of data.");
		}

		internal void resync(bool reload) {
			if (calendar!=null)
				calendar.Resync(true);
		}

		#endregion

		#region Find

		/// <summary> 
		/// Searches for the first Appointment within given time (hours, minutes).
		/// </summary> 
		/// <remarks> 
		/// This method searches for the first appointmen in the order 
		/// they are stored in a collection. 
		/// If no Appointment is found that contains an 
		/// hour given, null is returned.
		/// </remarks> 
		/// <param name="dateTime"> Date and hour searched for;
		/// It will be during and Appointment returned. 
		/// </param> 
		/// <returns>Returns an object located or null if no Appointment 
		/// contains a gived date/time 
		/// </returns> 
		public Appointment FindFirst(DateTime dateTime) {
			foreach (Appointment app in this)
				if (app.dateStart<=dateTime && dateTime<app.dateStop)
					return app;
			return null;
		}

		/// <summary> 
		/// Locates all CalendarAppointments going on at a given date/time. 
		/// </summary> 
		/// <remarks> 
		/// Appointment objects are copied to results array in the order 
		/// of the collection they are kept in.<para/> 
		/// If no objects containing a given date/hour are found 
		/// an empty array <see cref="Appointment.EmptyArray"/> is returned. 
		/// </remarks> 
		/// <param name="dateTime">
		/// Date an hour to be contained by objects returned.
		/// </param> 
		/// <returns>An array containing the object located. If no Appointment is found
		/// an empty array is returned.</returns> 
		public Appointment [] Find(DateTime dateTime) {
			ArrayList arr = null;
			foreach (Appointment app in this) {
				if (app.dateStart<=dateTime && dateTime<app.dateStop) {
					if (arr==null) arr = new ArrayList();
					arr.Add(app);
				}
			}
			if (arr==null) 
				return Appointment.EmptyArray;
			return (Appointment[])arr.ToArray(typeof(Appointment));
		}

		/// <summary> 
		/// Returns an array of CalendarAppointments selected as a collection. 
		/// </summary> 
		/// <remarks> 
		/// Property allows for reading all CalendarAppointments that are selected. 
		/// If there are no selected CalendarAppointments, a value 
		/// <see cref="Appointment.EmptyArray"/> is returned in a collection. 
		/// <seealso cref="Appointment.Selected"/> 
		/// <seealso cref="SelectAll"/> 
		/// <seealso cref="UnselectAll"/> 
		/// </remarks> 
		public Appointment [] Selected {
			get {
				int count = 0;
				foreach (Appointment ap in this)
					if (ap.Selected)
						++count;
				if (count==0) return Appointment.EmptyArray;

				Appointment [] result = new Appointment[count];
				int i = 0;
				foreach (Appointment ap in this)
					if (ap.Selected)
						result[i++] = ap;

				return result;
			}
		}


		/// <summary> 
		/// Retuns in a collection the first selected Appointment. 
		/// </summary> 
		/// <remarks> 
		/// If no Appointment is selected, a property returns <b>null</b>. 
		/// <seealso cref="Appointment.Selected"/> 
		/// <seealso cref="Selected"/> 
		/// <seealso cref="LastSelected"/> 
		/// </remarks> 
		public Appointment FirstSelected {
			get {
				foreach (Appointment ap in this)
					if (ap.Selected)
						return ap;
				return null;
			}
		}

		/// <summary> 
		/// Retuns in a collection the last selected Appointment. 
		/// </summary> 
		/// <remarks> 
		/// If no Appointment is selected, a property returns <b>null</b>. 
		/// <seealso cref="Appointment.Selected"/> 
		/// <seealso cref="Selected"/> 
		/// <seealso cref="FirstSelected"/> 
		/// </remarks> 
		public Appointment LastSelected {
			get {
				for (int i = Count; --i>=0; )
					if (this[i].Selected)
						return this[i];
				return null;
			}
		}

		#endregion

		#region Operations

		/// <summary>
		/// Selects all the selected <see cref="Appointment"/> objects.
		/// <seealso cref="UnselectAll"/>
		/// <seealso cref="Appointment.Selected"/>
		/// </summary>
		public void SelectAll() {
			foreach (Appointment a in this)
				a.Selected = false;
		}

		/// <summary>
		/// Deselects all the selected <see cref="Appointment"/> objects.
		/// <seealso cref="SelectAll"/>
		/// <seealso cref="Appointment.Selected"/>
		/// </summary>
		public void UnselectAll() {
			foreach (Appointment a in this)
				a.Selected = false;
		}

		#endregion
	}

}
